package com.yungu.swift.api.base.handler;

import com.yungu.swift.api.base.Constants;
import com.yungu.swift.api.config.ApplicationConfig;
import com.yungu.swift.api.utils.RsaUtil;
import com.yungu.swift.base.ResponseData;
import com.yungu.swift.constants.MagicConstant;
import com.yungu.swift.utils.JsonUtils;
import com.yungu.swift.utils.Md5Utils;
import com.yungu.swift.utils.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;
import java.io.*;
import java.util.*;

import static com.yungu.swift.api.base.ApiErrorEnum.*;

/**
 * @author cuixiuyin
 * api接口前缀处理器 校验 sign、appid、noncestr 参数。以及签名认证
 */
public class ApiHandler extends HandlerInterceptorAdapter {

    private static final Logger logger = LoggerFactory.getLogger(ApiHandler.class);

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        response.setCharacterEncoding("utf-8");
        response.setContentType("application/json;charset=utf-8");
        PrintWriter out = response.getWriter();
        //请求是 application/json 格式数据
        if (null != request.getContentType() && request.getContentType().contains(MagicConstant.PARAMS_JSON)) {
            // 1. 校验签名参数
            String sign = request.getHeader(Constants.SIGN_KEY);
            if (StringUtils.isBlank(sign)) {
                sign = request.getParameter(Constants.SIGN_KEY);
            }
            if (StringUtils.isBlank(sign)) {
                outPrint(out, JsonUtils.writeJsonStr(ResponseData.buildErrorResponse(NOT_SIGN.getCode(), NOT_SIGN.getMsg())));
                return false;
            }
            // 2. 校验 appid
            String appid = request.getHeader(Constants.APPID_KEY);
            if (StringUtils.isBlank(appid)) {
                appid = request.getParameter(Constants.APPID_KEY);
            }
            if (StringUtils.isBlank(appid)) {
                outPrint(out, JsonUtils.writeJsonStr(ResponseData.buildErrorResponse(NOT_APPID.getCode(), NOT_APPID.getMsg())));
                return false;
            }
            // 3. 校验随机数
            String noncestr = request.getHeader(Constants.NONCESTR_KEY);
            if (StringUtils.isBlank(noncestr)) {
                noncestr = request.getParameter(Constants.NONCESTR_KEY);
            }
            if (StringUtils.isBlank(noncestr)) {
                outPrint(out, JsonUtils.writeJsonStr(ResponseData.buildErrorResponse(NOT_NONCESTR.getCode(), NOT_NONCESTR.getMsg())));
                return false;
            }
            String bodyStr = getBodyString(request.getReader());
            logger.info("----------url:{}", request.getRequestURI());
            Map<String, Object> checkSignMap = checkSignByJson(sign, appid, noncestr, bodyStr);
            if (!Boolean.parseBoolean(checkSignMap.get("status").toString())) {
                outPrint(out, JsonUtils.writeJsonStr(ResponseData.buildErrorResponse(SIGN_ERROR.getCode(), SIGN_ERROR.getMsg() + ":" + checkSignMap.get("sign"))));
                return false;
            }
        }
        // 请求是 application/mixed 的数据
        else if (null != request.getContentType() && request.getContentType().contains("mixed")) {
            Part signPart = request.getPart(Constants.SIGN_KEY);
            Part tokenPart = request.getPart(Constants.TOKEN_KEY);
            Part appidPart = request.getPart(Constants.APPID_KEY);
            Part noncestrPart = request.getPart(Constants.NONCESTR_KEY);
            Map<String, String[]> paramMap = new HashMap<>(8);
            paramMap.put(signPart.getName(), new String[]{stream2Str(signPart.getInputStream())});
            paramMap.put(tokenPart.getName(), new String[]{stream2Str(tokenPart.getInputStream())});
            paramMap.put(appidPart.getName(), new String[]{stream2Str(appidPart.getInputStream())});
            paramMap.put(noncestrPart.getName(), new String[]{stream2Str(noncestrPart.getInputStream())});
            Map<String, Object> checkSignMap = checkAuthentication(paramMap);
            if (!Boolean.parseBoolean(checkSignMap.get("status").toString())) {
                outPrint(out, JsonUtils.writeJsonStr(ResponseData.buildErrorResponse(SIGN_ERROR.getCode(), SIGN_ERROR.getMsg() + ":" + checkSignMap.get("sign"))));
                return false;
            }
        }
        // 请求时 form 表单数据
        else {
            Map<String, String[]> paramMap = request.getParameterMap();
            if (!paramMap.containsKey(Constants.SIGN_KEY)) {
                outPrint(out, JsonUtils.writeJsonStr(ResponseData.buildErrorResponse(NOT_SIGN.getCode(), NOT_SIGN.getMsg())));
                return false;
            } else if (!paramMap.containsKey(Constants.APPID_KEY)) {
                outPrint(out, JsonUtils.writeJsonStr(ResponseData.buildErrorResponse(NOT_APPID.getCode(), NOT_APPID.getMsg())));
                return false;
            } else if (!paramMap.containsKey(Constants.NONCESTR_KEY)) {
                outPrint(out, JsonUtils.writeJsonStr(ResponseData.buildErrorResponse(NOT_NONCESTR.getCode(), NOT_NONCESTR.getMsg())));
                return false;
            }
            Map<String, Object> checkSignMap = checkAuthentication(paramMap);
            if (!Boolean.parseBoolean(checkSignMap.get("status").toString())) {
                outPrint(out, JsonUtils.writeJsonStr(ResponseData.buildErrorResponse(SIGN_ERROR.getCode(), SIGN_ERROR.getMsg() + ":" + checkSignMap.get("sign"))));
                return false;
            }
        }
        response.reset();
        return true;
    }


    /**
     * 输出错误
     *
     * @param jsonstr
     */
    private void outPrint(PrintWriter out, String jsonstr) {
        if (ApplicationConfig.SERVER_CONFIG.getSafety().getEncrypt()) {
            out.print(RsaUtil.doUrlAndEncrypt(jsonstr));
        } else {
            out.print(jsonstr);
        }
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object o, ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object o, Exception e) throws Exception {

    }

    /**
     * 检查参数加密是否一致
     *
     * @param paramMap
     * @return
     */
    private Map<String, Object> checkAuthentication(Map<String, String[]> paramMap) {
        String sign = paramMap.get(Constants.SIGN_KEY)[0];
        Collection<String> keyset = paramMap.keySet();
        List<String> list = new ArrayList<>(keyset);
        Collections.sort(list);
        String paramEncodeStr = "";
        for (int i = 0; i < list.size(); i++) {
            String paramKey = list.get(i);
            if (!Constants.SIGN_KEY.equals(paramKey)) {
                String paramVal = paramMap.get(list.get(i))[0];
                if (StringUtils.isNotEmpty(paramVal)) {
                    paramEncodeStr += list.get(i) + "=" + StringUtils.stripToEmpty(paramVal) + "&";
                }
            }
        }
        paramEncodeStr = paramEncodeStr + "key=" + Constants.AUTH_KEY;
        String encode = Md5Utils.encode(paramEncodeStr);
        logger.info("签名内容：{}", paramEncodeStr);
        logger.info("签名：{}", encode);
        Map<String, Object> result = new HashMap<>();
        if (sign.equalsIgnoreCase(encode)) {
            result.put("status", true);
            return result;
        }
        result.put("status", false);
        result.put("sign", encode);
        return result;
    }

    /**
     * JSON格式请求参数加密是否一致
     *
     * @param sign
     * @return
     */
    private Map<String, Object> checkSignByJson(String sign, String appid, String noncestr, String jsonStr) {
        Map<String, Object> hander = new HashMap<>(16);
        hander.put("appid", appid);
        hander.put("noncestr", noncestr);
        hander.put("key", Constants.AUTH_KEY);
        String encodeMd5 = Md5Utils.encode(JsonUtils.toJSONString(hander) + jsonStr);
        logger.info("---------content:{},{}", JsonUtils.toJSONString(hander), jsonStr);
        logger.info("---------------------------------encodeMd5:{}", encodeMd5);
        logger.info("---------------------------------sign:{}", sign);
        hander.clear();
        if (sign.equalsIgnoreCase(encodeMd5)) {
            hander.put("status", true);
            return hander;
        }
        hander.put("status", false);
        hander.put("sign", encodeMd5);
        return hander;
    }

    private String getBodyString(BufferedReader br) throws IOException {
        StringBuffer buffer = new StringBuffer();
        String line;
        while ((line = br.readLine()) != null) {
            buffer.append(line);
        }
        br.close();
        return buffer.toString();
    }

    private String stream2Str(InputStream inputStream) {
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"))) {
            String line;
            StringBuffer buffer = new StringBuffer();
            while ((line = reader.readLine()) != null) {
                buffer.append(line);
            }
            return buffer.toString();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return "";
    }
}
